package com.foreveross.proxyip.local;

import com.foreveross.crawl.common.util.RegHtmlUtil;
import com.google.common.collect.Lists;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.lang.math.RandomUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * 代理IP下载类。
 *
 * @author luomingliang@foreveross.com
 * @version 1.0.0
 */
public class HttpDownProxyIp {

    public Logger logger = LoggerFactory.getLogger(HttpDownProxyIp.class);
    private static final String IP_REGEX = "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}:\\d{1,5}$";

    // IP下载重试次数。
    private final static int c = 4;

    private final static int timeout = 10000;

    /* 单例 */
    private static HttpDownProxyIp _instance = null;

    public synchronized static HttpDownProxyIp getInstance() {
        if (_instance == null) {
            _instance = new HttpDownProxyIp();
        }
        return _instance;
    }

    /**
     * 随机从各IP提供商下载指定数量Ip 。
     *
     * @param num
     * @return
     * @throws Exception
     */
    public List<String> downloadIps(int num) throws Exception {
        return downloadIps(num, false);
    }

    /**
     * 随机从各IP提供商下载指定数量Ip 。
     *
     * @param num
     * @param isInter 是否为国际IP
     * @return
     * @throws Exception
     */
    public List<String> downloadIps(int num, boolean isInter) throws Exception {
        List<String> proxyips = new ArrayList<String>();
        if (!isInter) {
            logger.info("开始下载国内代理IP");
            proxyips = downloadDomeIps(num); // 下载国内IP。
        } else {
            logger.info("开始下载国际代理IP");
            proxyips = downloadInterIps(num);// 下载国际IP。
        }
        if (proxyips.size() == 0) {
            logger.warn("IP提取失败！");
        }
        return proxyips;
    }

    /**
     * 下载国内IP
     *
     * @param num
     * @return
     * @throws Exception
     */
    private List<String> downloadDomeIps(int num) throws Exception {
        List<String> proxyips = new ArrayList<String>();
        int i = c;
        while (i-- > 0) {
            int rd = RandomUtils.nextInt(domeIpAddressList.size());
            String name = Lists.newArrayList(domeIpAddressList.keySet()).get(rd);
            String url = Lists.newArrayList(domeIpAddressList.values()).get(rd);
            logger.info(String.format("提取IP源：%s", name));
            try {
                proxyips.addAll(downloadProxyIpsByUrl(url, num));
            } catch (Exception e) {
                logger.error(String.format("[%s] 提取IP出错[%s]", name, ExceptionUtils.getFullStackTrace(e)));
            }
            if (!proxyips.isEmpty() && proxyips.size() >= num) {
                break;
            }
        }
        return proxyips;
    }

    /**
     * 下载国际Ip
     *
     * @param num
     * @return
     * @throws Exception
     */
    private List<String> downloadInterIps(int num) throws Exception {
        List<String> proxyips = new ArrayList<String>();
        int i = c;
        while (i-- > 0) {
            int rd = RandomUtils.nextInt(interIpAddressList.size());
            String name = Lists.newArrayList(interIpAddressList.keySet()).get(rd);
            String url = Lists.newArrayList(interIpAddressList.values()).get(rd);
            logger.info(String.format("提取IP源：%s", name));
            try {
                proxyips.addAll(downloadProxyIpsByUrl(url, num));
            } catch (Exception e) {
                logger.error(String.format("[%s] 提取IP出错[%s]", name, ExceptionUtils.getFullStackTrace(e)));
            }
            if (!proxyips.isEmpty() && proxyips.size() >= num) {
                break;
            }
        }
        return proxyips;
    }

    /**
     * 下载代理IP。
     *
     * @param url
     * @return
     * @throws Exception
     */
    private List<String> downloadProxyIpsByUrl(String url, int num) throws Exception {
        List<String> proxyips = Lists.newArrayList();
        DefaultHttpClient httpClient = new DefaultHttpClient();
        httpClient.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, timeout);
        httpClient.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT, timeout);
        String html;
        try {
            url = url.replace("{num}", String.valueOf(num));
            html = EntityUtils.toString(httpClient.execute(new HttpGet(url)).getEntity());
            if (org.apache.commons.lang3.StringUtils.isNotBlank(html)) {
                for (String s : html.split("\n")) {
                    if (org.apache.commons.lang3.StringUtils.isNotEmpty(s) && RegHtmlUtil.regMathcher(s, IP_REGEX)) {
                        proxyips.add(s.replaceAll("\\s", ""));
                    }
                }
            }
            logger.info(String.format("本次提取数量:%s", proxyips.size()));
        } catch (Exception e) {
            throw e;
        } finally {
            httpClient.close();
        }
        return proxyips;
    }

    // 国际代理IP下载地址。
    private HashMap<String, String> interIpAddressList = new HashMap<String, String>() {
        {
            this.put("欣欣代理", "http://www.xinxinproxy.com/httpip/text?orderId=196622308412141&count={num}&isNew=1&ports=80,8080,8089,6666,6675&isForeign=1");
            this.put("IP海代理", "http://daili.iphai.com/getapi.ashx?ddh=659172589698529&num={num}&area=美国&port=&yys=1&am=0&mt=0&fm=text");
            this.put("2222代理", "http://60.173.11.232:2222/api.asp?ddbh=571184908488529&dq=%D6%D0%B9%FA&killdk=&dk=9064%2C80%2C8080%2C8088%2C8118%2C3128%2C18186%2C9999%2C8888%2C8585%2C8000&old=&sl=10&kt=&sub=%CC%E1+%BD%BB");
            this.put("TK代理", "http://www.tkdaili.com/api/getiplist.aspx?vkey=0C81656CE14BADE7D33FC27042438AB6&num={num}&country=NN&style=3");
        }
    };

    // 国内代理IP下载地址 。
    private HashMap<String, String> domeIpAddressList = new HashMap<String, String>() {
        {
            this.put("欣欣代理", "http://www.xinxinproxy.com/httpip/text?count={num}&orderId=196622308412141&isNew=1&ports=80,8080,8089,6666,6675&isForeign=0");
            this.put("IP海代理", "http://daili.iphai.com/getapi.ashx?ddh=659172589698529&num={num}&area=中国&port=&yys=1&am=0&mt=0&fm=text");
            this.put("2222代理", "http://60.173.11.232:2222/api.asp?ddbh=571184908488529&dk=8088%2C18186%2C8080%2C3128%2C80%2C1998%2C9999%2C9000%2C8888%2C63000%2C3129%2C1337%2C81%2C8000%2C808%2C1175%2C8081%2C8123%2C8118%2C443%2C8424&old=on&sl={num}");
            this.put("TK代理", "http://www.tkdaili.com/api/getiplist.aspx?vkey=0C81656CE14BADE7D33FC27042438AB6&num={num}&country=CN&style=3");
        }
    };

    public static void main(String[] arg) throws Exception {
       // List<String> proxyIps = HttpDownProxyIp.getInstance().downloadIps(1);
        List<String> proxyIps = HttpDownProxyIp.getInstance().downloadIps(10, true);
        for (String ip : proxyIps) {
            System.out.print("[" + ip + "] " + checkIpInfoByNet(ip));
            System.out.println();
        }
        // String result = checkIpInfoByNet("116.22.150.171");
        // System.out.println(result);
    }

    /**
     * 检测IP地址归属地。
     *
     * @param ip
     * @return
     * @throws IOException
     * @throws ClientProtocolException
     */
    private static String checkIpInfoByNet(String ip) {
        String checkAddr = "http://www.ip138.com/ips138.asp?ip=%s&action=2";
        DefaultHttpClient client = new DefaultHttpClient();
        HttpGet request = null;
        HttpEntity entity;
        try {
            request = new HttpGet(String.format(checkAddr, ip.substring(0, ip.indexOf(":"))));
            entity = client.execute(request).getEntity();
            String html = EntityUtils.toString(entity, "GBK");
            String result = RegHtmlUtil.regStr(html, "<li>本站主数据：(.*?)</li>");
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            request.abort();
            request = null;
            entity = null;
            client.close();
        }
    }
}